﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Security.Permissions;
using SPMS.Utility;
using SPMS.Log.Core;
using SPMS.Log.Repository;
using SPMS.Log.Utility;

namespace SPMS.Log
{
    /// <summary>
    /// 日志管理器
    /// </summary>
    /// <remarks>提供了对象日志记录器的管理</remarks>
    /// <history>
    /// [zanezeng]               2009/10/12 13:57    创建
    /// </history>
    public static class LogManager
    {
        #region ---Constructor

        /// <summary>
        /// 初始化类型<see cref="LogManager"/>
        /// </summary>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2010/1/6 13:39    创建
        /// </history>
        static LogManager()
        {
            //创建安全权限对象
            SecurityPermission permission = new SecurityPermission(SecurityPermissionFlag.ControlAppDomain);

            try
            {
                //要求授予了应用程序域控制权限
                permission.Demand();

                //挂接关闭通知事件
                AppDomain.CurrentDomain.DomainUnload += NotifyShutdown;
                AppDomain.CurrentDomain.ProcessExit += NotifyShutdown;
            }
            catch
            {
                //不抛出异常
            }
        }

        #endregion

        #region ---Public Method

        /// <summary>
        /// 获得默认的日志记录器
        /// </summary>
        /// <returns>获得的日志记录器</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/12 14:51    创建
        /// </history>
        public static ILog GetLogger()
        {
            //转调
            return GetLogger(null, string.Empty);
        }

        /// <summary>
        /// 获得给定名称的日志对象
        /// </summary>
        /// <param name="name">要获得注册为给定名称或者为给定名称上级名称的日志对象</param>
        /// <returns>注册为给定名称或者为给定名称上级名称的日志对象</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/12 14:46    创建
        /// </history>
        public static ILog GetLogger(string name)
        {
            //转调
            return GetLogger(null, name);
        }

        /// <summary>
        /// 获得给定名称的日志记录器
        /// </summary>
        /// <param name="configuration">用于创建日志存储器的配置信息</param>
        /// <param name="name">要获得的日志记录器器的名称</param>
        /// <returns>注册为给定名称或者为给定名称上级名称的日志对象</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/13 15:27    创建
        /// </history>
        public static ILog GetLogger(object configuration, string name)
        {
            try
            {
                //返回包装的日志记录器
                return WrapLogger(configuration, name);
            }
            catch (Exception ex)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error(ex, "根据日志存储配置信息'{0}'创建日志对象'{1}'失败", configuration, name);
                }

                //抛出异常
                throw;
            }
        }

        /// <summary>
        /// 向默认的日志存储器中注册给定的日志追加器
        /// </summary>
        /// <param name="name">日志追加器的注册名称</param>
        /// <param name="appender">要注册的日志追加器</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="appender"/>为Null</exception>
        /// <exception cref="InvalidOperationException">日志存储器不是可配置日志存储器</exception>
        /// <history>
        /// [zanezeng]               2009/10/15 9:31    创建
        /// </history>
        public static void RegisterAppender(string name, ILogAppender appender)
        {
            //转调
            RegisterAppender(null, name, appender);
        }

        /// <summary>
        /// 向给定的日志存储器中注册给定的日志追加器
        /// </summary>
        /// <param name="configuration">用于获得日志存储器的配置对象</param>
        /// <param name="name">日志追加器的注册名称</param>
        /// <param name="appender">要注册的日志追加器</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="appender"/>为Null</exception>
        /// <exception cref="InvalidOperationException"><paramref name="configuration"/>对应的日志存储器不是可配置日志存储器</exception>
        /// <history>
        /// [zanezeng]               2009/10/15 9:31    创建
        /// </history>
        public static void RegisterAppender(object configuration, string name, ILogAppender appender)
        {
            //断言参数
            ArgumentAssert.AssertArgumentNotNull(appender, "appender");

            //获得日志存储器
            ILoggingRepository repository = CreateRepository(configuration);

            //转化为可配置存储器
            IConfigurableLoggingRepository configurableRepository = repository as IConfigurableLoggingRepository;

            //判断日志存储器是否可以配置
            if (null == configurableRepository)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error("日志存储器'{0}'不是可配置日志储存器，无法注册日志追加器'{1}'", repository, appender);
                }

                //抛出异常
                throw new InvalidOperationException(string.Format(Properties.Resources.MSG_REPOSITORY_NOT_CONFIGURABLE, repository));
            }
            else
            {
                try
                {
                    //注册日志追加器
                    configurableRepository.RegisterAppender(appender);
                }
                catch (Exception ex)
                {
                    //输出日志
                    if (LoggingContext.MetaLog.IsErrorEnabled)
                    {
                        LoggingContext.MetaLog.Error(ex, "向日志存储器'{0}'中注册日志追加器'{1}'的过程中发生错误", repository, appender);
                    }

                    //抛出异常
                    throw;
                }
            }
        }

        /// <summary>
        /// 从给定的日志存储器中注销给定的日志追加器
        /// </summary>
        /// <param name="appender">要注销的日志追加器</param>
        /// <returns>
        /// 	<c>true</c>：给定的日志追加器注销成功<br/>
        /// 	<c>false</c>：给定的日志追加器注销失败
        /// </returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/15 9:31    创建
        /// </history>
        public static bool UnregisterAppender(ILogAppender appender)
        {
            return UnregisterAppender(null, appender);
        }

        /// <summary>
        /// 从给定的日志存储器中注销给定的日志追加器
        /// </summary>
        /// <param name="configuration">用于获得日志存储器的配置对象</param>
        /// <param name="appender">要注销的日志追加器</param>
        /// <returns>
        /// 	<c>true</c>：给定的日志追加器注销成功<br/>
        /// 	<c>false</c>：给定的日志追加器注销失败
        /// </returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/15 9:31    创建
        /// </history>
        public static bool UnregisterAppender(object configuration, ILogAppender appender)
        {
            //判断要注销的日志存储器是否为NULL
            if (null == appender)
            {
                //如果为NULL，则直接返回注销失败
                return false;
            }

            //获得日志存储器
            ILoggingRepository repository = CreateRepository(configuration);

            //转化为可配置存储器
            IConfigurableLoggingRepository configurableRepository = repository as IConfigurableLoggingRepository;

            //判断日志存储器是否可以配置
            if (null == configurableRepository)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error("日志存储器'{0}'不是可配置日志储存器，无法注册日志追加器'{1}'", repository, appender);
                }

                //如果不是可配置日志存储器，则直接返回注销失败
                return false;
            }
            else
            {
                //注销日志追加器
                return configurableRepository.UnregisterAppender(appender);
            }
        }

        /// <summary>
        /// 从给定的日志存储器中注销给定的日志追加器
        /// </summary>
        /// <param name="appenderName">要注销的日志追加器的名称</param>
        /// <returns>被注销的日志追加器</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/15 9:31    创建
        /// </history>
        public static ILogAppender UnregisterAppender(string appenderName)
        {
            return UnregisterAppender(null, appenderName);
        }

        /// <summary>
        /// 从给定的日志存储器中注销给定的日志追加器
        /// </summary>
        /// <param name="configuration">用于获得日志存储器的配置对象</param>
        /// <param name="appenderName">要注销的日志追加器的名称</param>
        /// <returns>被注销的日志追加器</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/15 9:31    创建
        /// </history>
        public static ILogAppender UnregisterAppender(object configuration, string appenderName)
        {
            //判断要注销的日志存储器的名称是否为NULL或者为空字符串
            if (string.IsNullOrEmpty(appenderName))
            {
                //如果为NULL或者为空字符串，则直接返回NULL
                return null;
            }

            //获得日志存储器
            ILoggingRepository repository = CreateRepository(configuration);

            //转化为可配置存储器
            IConfigurableLoggingRepository configurableRepository = repository as IConfigurableLoggingRepository;

            //判断日志存储器是否可以配置
            if (null == configurableRepository)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error("日志存储器'{0}'不是可配置日志储存器，无法注册日志追加器'{1}'", repository, appenderName);
                }

                //返回注销失败
                return null;
            }
            else
            {
                //注销日志追加器
                return configurableRepository.UnregisterAppender(appenderName);
            }
        }

        /// <summary>
        /// 向给定的日志存储器中注册日志记录器
        /// </summary>
        /// <param name="name">要注册的日志记录器的名称</param>
        /// <param name="level">日志记录器允许输出的最低日志级别，如果为NULL，则默认为日志存储器的默认日志级别</param>
        /// <param name="isDefault">是否设置为默认日志记录器</param>
        /// <param name="referAppenders">要注册的日志记录器引用的日志追加器</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="name"/>为Null或者为空字符串</exception>
        /// <exception cref="ArgumentException"><paramref name="referAppenders"/>引用不存在的日志追加器</exception>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/16 11:06    创建
        /// </history>
        public static void RegisterLogger(string name, Level level, bool isDefault, params string[] referAppenders)
        {
            RegisterLogger(null, name, level, isDefault, referAppenders);
        }

        /// <summary>
        /// 向给定的日志存储器中注册日志记录器
        /// </summary>
        /// <param name="configuration">用于获得日志存储器的配置对象</param>
        /// <param name="name">要注册的日志记录器的名称</param>
        /// <param name="level">日志记录器允许输出的最低日志级别，如果为NULL，则默认为日志存储器的默认日志级别</param>
        /// <param name="isDefault">是否设置为默认日志记录器</param>
        /// <param name="referAppenders">要注册的日志记录器引用的日志追加器</param>
        /// <remarks></remarks>
        /// <exception cref="ArgumentNullException"><paramref name="name"/>为Null或者为空字符串</exception>
        /// <exception cref="ArgumentException"><paramref name="referAppenders"/>引用不存在的日志追加器</exception>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/16 11:06    创建
        /// </history>
        public static void RegisterLogger(object configuration, string name, Level level, bool isDefault, params string[] referAppenders)
        {
            //断言参数
            ArgumentAssert.AssertStringArgumentNotNullOrEmpty(name, "name");

            //获得日志存储器
            ILoggingRepository repository = CreateRepository(configuration);

            //转化为可配置存储器
            IConfigurableLoggingRepository configurableRepository = repository as IConfigurableLoggingRepository;

            //判断日志存储器是否可以配置
            if (null == configurableRepository)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error("日志存储器'{0}'不是可配置日志储存器，无法注册日志记录器'{1}'", repository, name);
                }

                //抛出异常
                throw new InvalidOperationException(string.Format(Properties.Resources.MSG_REPOSITORY_NOT_CONFIGURABLE, repository));
            }
            else
            {
                try
                {
                    //注册日志追加器
                    configurableRepository.RegisterLogger(name, level, isDefault, referAppenders);
                }
                catch (Exception ex)
                {
                    //输出日志
                    if (LoggingContext.MetaLog.IsErrorEnabled)
                    {
                        LoggingContext.MetaLog.Error(ex, "向日志存储器'{0}'中注册日志记录器'{1}'的过程中发生错误", repository, name);
                    }

                    //抛出异常
                    throw;
                }
            }
        }

        /// <summary>
        /// 注销给定名称的日志记录器
        /// </summary>
        /// <param name="name">要注销的日志记录器的名称</param>
        /// <returns>
        /// 	<c>true</c>：给定名称的日志记录器注销成功<br/>
        /// 	<c>false</c>：给定名称的日志记录器不存在
        /// </returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/16 11:06    创建
        /// </history>
        public static bool UnregisterLogger(string name)
        {
            return UnregisterLogger(null, name);
        }

        /// <summary>
        /// 注销给定名称的日志记录器
        /// </summary>
        /// <param name="configuration">用于获得日志存储器的配置对象</param>
        /// <param name="name">要注销的日志记录器的名称</param>
        /// <returns>
        /// 	<c>true</c>：给定名称的日志记录器注销成功<br/>
        /// 	<c>false</c>：给定名称的日志记录器不存在
        /// </returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/16 11:06    创建
        /// </history>
        public static bool UnregisterLogger(object configuration, string name)
        {
            //判断要注销的日志存储器的名称是否为NULL或者为空字符串
            if (string.IsNullOrEmpty(name))
            {
                //如果为NULL或者为空字符串，则返回注销失败
                return false;
            }

            //获得日志存储器
            ILoggingRepository repository = CreateRepository(configuration);

            //转化为可配置存储器
            IConfigurableLoggingRepository configurableRepository = repository as IConfigurableLoggingRepository;

            //判断日志存储器是否可以配置
            if (null == configurableRepository)
            {
                //输出日志
                if (LoggingContext.MetaLog.IsErrorEnabled)
                {
                    LoggingContext.MetaLog.Error("日志存储器'{0}'不是可配置日志储存器，无法注销日志记录器'{1}'", repository, name);
                }

                //返回注销失败
                return false;
            }
            else
            {
                //注销日志追加器
                return configurableRepository.UnregisterLogger(name);
            }
        }

        /// <summary>
        /// 获得默认的日志存储器
        /// </summary>
        /// <returns>默认的日志存储器</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/13 15:39    创建
        /// </history>
        public static ILoggingRepository GetRepository()
        {
            //创建日志存储器
            return CreateRepository(null);
        }

        /// <summary>
        /// 获得根据给定的配置信息创建日志存储器
        /// </summary>
        /// <param name="configuration">用于创建日志存储器的配置信息</param>
        /// <returns>根据给定的配置信息创建日志存储器</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/14 10:43    创建
        /// </history>
        public static ILoggingRepository GetRepository(object configuration)
        {
            //创建日志存储器
            return CreateRepository(configuration);
        }

        /// <summary>
        /// 关闭当前日志管理器
        /// </summary>
        /// <remarks>用于程序退出时的清场处理</remarks>
        /// <history>
        /// [zanezeng]               2010/1/6 13:45    创建
        /// </history>
        public static void Shutdown()
        {
            //清除日志存储器工厂
            LogManager.RepositoryFactory = null;
        }

        #endregion

        #region ---Private Method

        /// <summary>
        /// 包装给定的日志记录器
        /// </summary>
        /// <param name="configuration">用于创建日志存储器的配置信息</param>
        /// <param name="name">要获得的日志记录器器的名称</param>
        /// <returns>被包装后的日志记录器</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/16 11:39    创建
        /// </history>
        private static ILog WrapLogger(object configuration, string name)
        {
            //用于保存缓存键
            int cacheKey = KEY_DEFAULTREPOSITORY.GetHashCode();

            //判断配置是否为NULL
            if (null != configuration)
            {
                //获得缓存键
                cacheKey = configuration.GetHashCode();
            }

            //判断日志记录器的名称是否为NULL
            if (null == name)
            {
                //如果日志记录器的名称为NULL，则设置为空字符串
                name = string.Empty;
            }

            //用于保存配置信息对应的日志存储器日志记录器缓存
            SynchronizedDictionary<string, ILog> logs = null;

            //尝试获得日志对象缓存
            if (s_LogsCache.TryGetValue(cacheKey, out logs))
            {
                //创建日志对象
                return CreateLog(configuration, name, logs);
            }

            //输出日志
            if (LoggingContext.MetaLog.IsTraceEnabled)
            {
                LoggingContext.MetaLog.Trace("没有从缓存中找到日志配置'{0}'的日志对象缓存，开始创建日志配置'{0}'的日志对象缓存", configuration);
            }

            //创建日志对象缓存
            logs = new SynchronizedDictionary<string, ILog>(StringComparer.InvariantCultureIgnoreCase);

            //存储日志对象
            s_LogsCache[cacheKey] = logs;

            //创建日志对象
            ILog log = CreateLog(configuration, name, logs);

            //输出日志
            if (LoggingContext.MetaLog.IsTraceEnabled)
            {
                LoggingContext.MetaLog.Trace("没有从缓存中找到日志配置'{0}'的日志对象缓存，日志配置'{0}'的日志对象缓存创建完毕", configuration);
            }

            //返回创建的日志对象
            return log;
        }

        /// <summary>
        /// 创建日志对象
        /// </summary>
        /// <param name="configuration">用于创建日志存储器的配置信息</param>
        /// <param name="name">要创建的日志对象的名称</param>
        /// <param name="logs">用于保存日志对象的缓存</param>
        /// <returns>获得的日志对象</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/16 12:04    创建
        /// </history>
        private static ILog CreateLog(object configuration, string name, SynchronizedDictionary<string, ILog> logs)
        {
            //用于保存创建的日志对象
            ILog log = null;

            //获得日志存储器
            ILoggingRepository repository = CreateRepository(configuration);

            //判断日志日志记录器是否已经缓存
            if (logs.TryGetValue(name, out log))
            {
                //判断日志记录器是否有效
                if (null != log && null != log.Logger)
                {
                    //判断日志存储器是否发生变更
                    if (object.ReferenceEquals(repository, log.Logger.Repository))
                    {
                        //输出日志
                        if (LoggingContext.MetaLog.IsTraceEnabled)
                        {
                            LoggingContext.MetaLog.Trace("从缓存中找到日志记录器'{0}'，返回从缓存中找到的日志记录器", name);
                        }

                        //如果没有变更，则返回缓存的日志对象
                        return log;
                    }
                }
            }

            //输出日志
            if (LoggingContext.MetaLog.IsTraceEnabled)
            {
                LoggingContext.MetaLog.Trace("没有从缓存中找到日志对象'{0}'，开始创建日志对象'{0}'", name);
            }

            //创建日志记录器
            ILogger logger = new LoggerAdapter(repository, name);

            //创建日志记录器适配器
            log = new LogImplement(logger);

            //保存创建的日志记录器
            logs[name] = log;

            //输出日志
            if (LoggingContext.MetaLog.IsTraceEnabled)
            {
                LoggingContext.MetaLog.Trace("日志对象'{0}'创建完毕", name);
            }

            //返回日志对象
            return log;
        }

        /// <summary>
        /// 根据给定配置对象创建日志存储器
        /// </summary>
        /// <param name="configuration">日志存储器的配置信息</param>
        /// <returns>创建的日志存储器</returns>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2009/10/19 14:07    创建
        /// </history>
        private static ILoggingRepository CreateRepository(object configuration)
        {
            //调用日志存储器工厂创建日志存储器
            return LogManager.RepositoryFactory.GetRepository(configuration);
        }

        /// <summary>
        /// 通知关闭当前日志管理器
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">包含事件数据的<see cref="System.EventArgs"/>实例</param>
        /// <remarks></remarks>
        /// <history>
        /// [zanezeng]               2010/1/6 13:44    创建
        /// </history>
        private static void NotifyShutdown(object sender, EventArgs e)
        {
            //关闭日志管理器
            LogManager.Shutdown();
        }

        #endregion

        #region ---Property

        /// <summary>
        /// 获得或者设置日志存储器工厂
        /// </summary>
        /// <value>日志存储器工厂</value>
        /// <remarks></remarks>
        /// <exception cref="ArgumentException">设置的日志存储器工厂已经被处置</exception>
        /// <history>
        /// [zanezeng]               2009/10/13 15:41    创建
        /// </history>
        public static IRepositoryFactory RepositoryFactory
        {
            get
            {
                //判断是否设置了日志存储器工厂并且日志存储器工厂是否已经关闭
                if (null == s_RepositoryFactory || s_RepositoryFactory.IsClosed)
                {
                    //同步
                    lock (SYNCROOT)
                    {
                        //判断是否设置了日志存储器工厂
                        if (null == s_RepositoryFactory || s_RepositoryFactory.IsClosed)
                        {
                            //创建默认的日志存储器工厂
                            s_RepositoryFactory = new DefaultRepositoryFactory();
                        }
                    }
                }

                //返回创建的日志存储器工厂
                return s_RepositoryFactory;
            }
            set
            {
                //判断是否为用一个实例
                if (object.ReferenceEquals(value, LogManager.RepositoryFactory))
                {
                    //如果是同一个实例，则直接返回
                    return;
                }

                //判断指定的日志存储器工厂是否已经被处置
                if (null != value && value.IsClosed)
                {
                    //抛出异常
                    throw new ArgumentException(Properties.Resources.MSG_REPOSITORYFACTORY_CLOSED);
                }

                //同步
                lock (SYNCROOT)
                {
                    //保存当前的日志存储器工厂
                    IRepositoryFactory previousFactory = s_RepositoryFactory;

                    //设置当前的日志存储器工厂
                    s_RepositoryFactory = value;

                    //判断是否需要销毁前一个日志存储器工厂
                    if (null != previousFactory && !previousFactory.IsClosed)
                    {
                        //如果需要销毁，则销毁日志存储器工厂
                        previousFactory.Close();
                    }
                }
            }
        }

        #endregion

        #region ---Field

        /// <summary>
        /// 日志记录器缓存
        /// </summary>
        private static SynchronizedDictionary<int, SynchronizedDictionary<string, ILog>> s_LogsCache = new SynchronizedDictionary<int, SynchronizedDictionary<string, ILog>>();

        /// <summary>
        /// 日志存储器工厂
        /// </summary>
        private static IRepositoryFactory s_RepositoryFactory;

        /// <summary>
        /// 同步对象
        /// </summary>
        private static readonly object SYNCROOT = new object();

        /// <summary>
        /// 默认的日志存储器键
        /// </summary>
        private static readonly int KEY_DEFAULTREPOSITORY = int.MinValue;

        #endregion
    }
}
